use crate::figure::{
canvas::{pixelcanvas::PixelCanvas, svgcanvas::SvgCanvas},
figuretypes::histogram::Histogram,
utilities::axistype::AxisType,
};
use super::drawer::Drawer;
impl Drawer for Histogram {
fn draw_svg(&mut self, svg_canvas: &mut SvgCanvas) {
let width = svg_canvas.width as f64;
let height = svg_canvas.height as f64;
let margin = svg_canvas.margin as f64;
let font_size = 12.0;
svg_canvas.draw_rect(0.0, 0.0, width, height, "white", "black", 1.0, 1.0);
svg_canvas.draw_title(
width / 2.0,
margin / 2.0,
&self.title,
font_size * 2.0,
"black",
);
let y_max = self.bin_counts.iter().cloned().fold(0.0, f64::max);
let scale_x = (width - 2.0 * margin) / (self.max - self.min);
let scale_y = (height - 2.0 * margin) / y_max;
let num_ticks = 10;
svg_canvas.draw_grid(
margin,
width - margin,
margin,
height - margin,
num_ticks,
num_ticks,
"lightgray",
);
let origin_x = margin;
let origin_y = height - margin;
svg_canvas.draw_line(margin, origin_y, width - margin, origin_y, "black", 2.0); svg_canvas.draw_line(margin, margin, margin, height - margin, "black", 2.0); let mut x_axis_ticks = String::new();
for i in 0..=num_ticks {
let value = self.max + i as f64 * (self.max - self.min) / num_ticks as f64;
let x = margin + i as f64 * (width - 2.0 * margin) / num_ticks as f64;
let tick_start_y = origin_y - 5.0;
let tick_end_y = origin_y + 5.0;
x_axis_ticks.push_str(&format!(
"M {:.2},{:.2} L {:.2},{:.2} ",
x, tick_start_y, x, tick_end_y
));
svg_canvas.elements.push(format!(
r#"<text x="{:.2}" y="{:.2}" font-size="{:.2}" text-anchor="middle" fill="black">{:.1}</text>"#,
x, height - margin + font_size * 1.5, font_size, value));
}
svg_canvas.elements.push(format!(
r#"<path d="{}" stroke="black" stroke-width="1" fill="none"/>"#,
x_axis_ticks
));
let mut y_axis_ticks = String::new();
for i in 0..=num_ticks {
let value = 0.0 + i as f64 * (y_max - 0.0) / num_ticks as f64;
let y = height - margin - i as f64 * (height - 2.0 * margin) / num_ticks as f64;
let tick_start_x = origin_x - 5.0;
let tick_end_x = origin_x + 5.0;
y_axis_ticks.push_str(&format!(
"M {:.2},{:.2} L {:.2},{:.2} ",
tick_start_x, y, tick_end_x, y
));
svg_canvas.elements.push(format!(
r#"<text x="{:.2}" y="{:.2}" font-size="{:.2}" text-anchor="end" fill="black">{:.1}</text>"#,
margin - 5.0, y + font_size * 0.3, font_size, value
));
}
svg_canvas.elements.push(format!(
r#"<path d="{}" stroke="black" stroke-width="1" fill="none"/>"#,
y_axis_ticks
));
svg_canvas.draw_text(
width / 2.0,
height - margin / 4.0,
&self.x_label,
font_size * 1.5,
"black",
);
svg_canvas.elements.push(format!(
r#"<text x="{:.2}" y="{:.2}" font-size="{:.2}" text-anchor="middle" fill="black" transform="rotate(-90 {:.2} {:.2})">{}</text>"#,
margin / 3.0,
height / 2.0,
font_size * 1.5,
margin / 3.0,
height / 2.0,
self.y_label
));
for (i, &count) in self.bin_counts.iter().enumerate() {
let bin_start = self.min + i as f64 * self.bin_width;
let bin_end = bin_start + self.bin_width;
let x_start = margin + (bin_start - self.min) * scale_x;
let x_end = margin + (bin_end - self.min) * scale_x;
let bar_width = x_end - x_start;
let bar_height = count * scale_y;
svg_canvas.draw_rect(
x_start,
origin_y - bar_height,
bar_width,
bar_height,
&format!("rgb({},{},{})", self.color[0], self.color[1], self.color[2]),
"black",
1.0,
1.0,
);
}
}
fn draw(&mut self, canvas: &mut PixelCanvas) {
canvas.clear();
let margin = canvas.margin;
let width = canvas.width;
let height = canvas.height;
let cfg = &self.config;
self.draw_title(canvas, &cfg, width / 2, margin / 2, &self.title);
let bin_data = self.calculate_bins();
let y_max = bin_data.iter().map(|&(_, freq)| freq).fold(0.0, f64::max);
let scale_x = (width - 2 * margin) as f64 / self.bins as f64;
let scale_y = (height - 2 * margin) as f64 / y_max;
canvas.draw_grid(
&[cfg.num_grid_horizontal, cfg.num_grid_vertical],
cfg.color_grid,
);
let origin_x = margin as i32;
let origin_y = height as i32 - margin as i32;
let bin_start = bin_data[0].0; let bin_width = (bin_data[1].0 - bin_start).abs(); for (i, &(_, freq)) in bin_data.iter().enumerate() {
let bar_height = (freq * scale_y) as i32;
let bar_left = origin_x + (i as f64 * scale_x) as i32;
let bar_right = bar_left + scale_x as i32;
for x in bar_left..=bar_right {
for y in (origin_y - bar_height)..origin_y {
canvas.draw_pixel(x as u32, y as u32, self.color);
}
}
let edge_color = [0, 0, 0]; for y in (origin_y - bar_height)..origin_y {
canvas.draw_pixel(bar_left as u32, y as u32, edge_color);
}
for y in (origin_y - bar_height)..origin_y {
canvas.draw_pixel(bar_right as u32, y as u32, edge_color);
}
for x in bar_left..=bar_right {
canvas.draw_pixel(x as u32, (origin_y - bar_height) as u32, edge_color);
}
}
for i in 0..=self.bins {
let edge_x = origin_x + (i as f64 * scale_x) as i32;
let edge_value = bin_start + i as f64 * bin_width;
canvas.draw_pixel(edge_x as u32, origin_y as u32, [0, 0, 0]); let edge_label = format!("{:.1}", edge_value);
self.draw_axis_value(
canvas,
cfg,
edge_x as u32,
origin_y as u32 + 10,
&edge_label,
AxisType::AxisX,
);
}
let num_y_ticks = 10;
for i in 0..=num_y_ticks {
let tick_value = y_max * i as f64 / num_y_ticks as f64;
let tick_y = origin_y - (tick_value * scale_y) as i32;
canvas.draw_pixel(origin_x as u32, tick_y as u32, [0, 0, 0]); let tick_label = format!("{:.1}", tick_value);
self.draw_axis_value(
canvas,
cfg,
origin_x as u32 - 10,
tick_y as u32,
&tick_label,
AxisType::AxisY,
);
}
let origin_y = height - margin;
self.draw_label(canvas, cfg, width - margin / 2, origin_y, &self.y_label);
self.draw_label(canvas, cfg, margin, margin / 2, &self.x_label);
canvas.draw_vertical_line(canvas.margin, [0, 0, 0]);
canvas.draw_vertical_line(canvas.width - canvas.margin, [0, 0, 0]);
canvas.draw_horizontal_line(canvas.height - canvas.margin, [0, 0, 0]);
canvas.draw_horizontal_line(canvas.margin, [0, 0, 0]);
}
fn draw_legend(&self, _canvas: &mut PixelCanvas) {
}
}